#include "FileSystem.h"
#include "Directory.h"

FileSystem::FileSystem(const char *device) {
	this->device = new Device(device);
	openFileList = new std::list<OpenFile*>();
	activeInodeTable = new std::map<int, Inode*>();
	SuperBlok* sb = this->device->getSuperBlok();

	amountInodeBlocks 	= sb->getAmountInodeBlocks();
	amountBlocks 		= sb->getAmountBlocks();
	delete sb;
}

FileSystem::~FileSystem() {
	for(map<int,Inode*>::iterator iter = activeInodeTable->begin(); iter != activeInodeTable->end(); ++iter){
		delete iter->second;
	}
	for(list<OpenFile*>::iterator iter = openFileList->begin(); iter != openFileList->end(); ++iter){
		delete *iter;
	}
	delete device;
	delete activeInodeTable;
	delete openFileList;
}

Inode * FileSystem::findInode(const char *absolute_path) {
	Inode * root = findInodeByNumber(ROOT);
	try{
		Directory * dir = new Directory(this, root, "/:");



	char path[512];
	strcpy(path, absolute_path);
	string strPath = path;
	
	if (strPath == "/")
		return root;

	Inode * openFileInode = 0;

	char *ptr = strtok(path, "/");
	while (ptr != NULL) {
		try{
		openFileInode = dir->findFile(ptr);
		} catch (exception& e){
			cerr << e.what();
			exit(1);
		}
		std::string filename = ptr;
		ptr = strtok(NULL, "/");
		if (ptr != NULL) {
			delete dir;
			dir = new Directory(this, openFileInode, filename);
		}
	}
	
	delete dir;
	
	return openFileInode;

	} catch (exception& e) {
		cerr << e.what();
		exit(1);
	}

}

Blok *FileSystem::getBlok(int number) {
	if(amountBlocks < number){
		throw "Block number is higher than the total amount of blocks.";
	}
	 return device->getBlok(number);
}

Inode *FileSystem::findInodeByNumber(int number){
	// check of inode al bestaat
	if (activeInodeTable->count(number) > 0) {
		Inode* inode = (*activeInodeTable)[number];
		inode->usecountIncrement();

		return inode;
	}

		if (amountInodeBlocks < itod(number)) {
			throw "Inode number is higher than the total amount of Isnode blocks.";
		}

	Blok *bp = getBlok(itod(number));
	byte buffer[INSIZ];
	Inode* inode = new Inode(this, bp->getBytes(buffer,itoo(number) * INSIZ, INSIZ), number);
	activeInodeTable->insert(make_pair(inode->getNumber(), inode));
	return inode;
}

OpenFile* FileSystem::open(const char* absolute_path) {
	std::string aboslutePath = absolute_path;
	std::string tmpString = aboslutePath.substr(aboslutePath.find_last_of("/") + 1);

	Inode* inode = findInode(absolute_path);

	list<OpenFile*>::iterator it;
	for (it = openFileList->begin(); it != openFileList->end(); ++it) {
		if ((*it)->getInode() == inode) {
			inode->release(); // omdat die met de findInode verhoogd is
			return (*it);
		}
	}

	OpenFile* newFile = new OpenFile(this, inode, tmpString);

	openFileList->push_front(newFile);

	return newFile;
}

int FileSystem::read(OpenFile* fileptr, char buffer[], int count) {
	return fileptr->read(buffer, count);
}

int FileSystem::lseek(OpenFile *fileptr, long offset, int whence) {
	return fileptr->lseek(offset, whence);
}

void FileSystem::close(OpenFile* fileptr) {
	fileptr->close();

	if (fileptr->getUsecount() == 0) {
		releaseInode(fileptr->getInode());
		openFileList->remove(fileptr);
		delete fileptr;
	}
}

void FileSystem::dumpTreeList() {
	std::list<Directory*>* directoryList = new list<Directory*> ();
	Directory* tmpDirectory;

	Inode* root = findInodeByNumber(ROOT);
	Directory* dir = new Directory(this, root, "/:");

	directoryList->push_back(dir);

	printf(" %5s %5s  %12s  %15s %8s ", "inum", "nlinks", "mode", "size", "name");

	while (directoryList->size() > 0) {
		tmpDirectory = directoryList->front();
		directoryList->pop_front();

		std::map<std::string, long>* inodeList = tmpDirectory->getInodeNummerLijst();

		std::cout << std::endl << tmpDirectory->getName() << std::endl;

		for (map<std::string, long>::iterator iter = inodeList->begin(); iter != inodeList->end(); ++iter) {
			Inode* tmpInode = findInodeByNumber(iter->second);
			std::string name = iter->first;

			printf(" %5d %5d  %12o  %15d ", iter->second, tmpInode->getNumberOfLinks(), tmpInode->getMode(), tmpInode->getFileSize());
			std::cout << "  " << name << endl;

			if ((tmpInode->getType() & X_IFMT) == X_IFDIR && iter->first != "." && iter->first != ".." )
				directoryList->push_back(new Directory(this, tmpInode, iter->first));

		}
		delete inodeList;
		delete tmpDirectory;
	}

	delete directoryList;
}

std::list<OpenFile*>* FileSystem::getSOFT() {
	return openFileList;
}

std::map<int, Inode*>* FileSystem::getAIT() {
	return activeInodeTable;
}

void FileSystem::releaseInode(Inode* inode) {
	inode->release();
	if (inode->getUsecount() == 0) {
		delete activeInodeTable->at(inode->getNumber());
		activeInodeTable->erase(inode->getNumber());
	}
}
